#include "listTimer.hpp"
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cassert>
#include <signal.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <cstdlib>
#include <sys/epoll.h>
using namespace std;

const int FD_LIMIT = 65535;
const int MAX_EVENT_NUMBER = 1024;
const int TIMESLOT = 5;

static int pipeFd[2];
static sortTimerLst timerList;
static int epollFd = 0;

int setNonBlocking (int fd) {
    int oldOption = fcntl(fd, F_GETFL);
    int newOption = oldOption | O_NONBLOCK;
    fcntl(fd, F_SETFL, newOption);
    return oldOption;
}

void addFd(int epollFd, int fd) {
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &event);
    setNonBlocking(fd);
}

void sigHandler(int sig) {
    int saveErrno = errno;  
    int msg = sig;
    write(pipeFd[1], &sig, sizeof(sig));
    errno = saveErrno;
}

void addsig(int sig) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigHandler;
    sa.sa_flags |= SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, nullptr) != -1);
}

void timerHandler() {
    timerList.tick();
    alarm(TIMESLOT);
}

void callBackFunc(clientData* userData) {
    assert(userData);
    epoll_ctl(epollFd, EPOLL_CTL_DEL, userData->sockFd, 0);
    close(userData->sockFd);
    cout << "close fd " << userData->sockFd << endl;
}

int main(int argc, char* argv[]) {
    if(argc <= 2) {
        cout << "Usage: " << argv[0] << " <ip> <port>" << endl;
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    int ret = 0;
    struct sockaddr_in address;
    memset(&address, 0, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int listenFd = socket(AF_INET, SOCK_STREAM, 0);
    assert(listenFd >= 0);

    ret = bind(listenFd, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);

    ret = listen(listenFd, 5);
    assert(ret != -1);

    epoll_event events[MAX_EVENT_NUMBER];
    int epollFd = epoll_create(5);
    assert(epollFd >= 0);
    addFd(epollFd, listenFd);

    ret = socketpair(AF_UNIX, SOCK_STREAM, 0, pipeFd);
    assert(ret != -1);
    setNonBlocking(pipeFd[1]);
    addFd(epollFd, pipeFd[0]);

    addsig(SIGALRM);
    addsig(SIGTERM);
    bool stopServer = false;

    clientData* users = new clientData[FD_LIMIT];
    bool timeout = false;
    alarm(TIMESLOT);

    while(!stopServer) {
        int number = epoll_wait(epollFd, events, MAX_EVENT_NUMBER, -1);
        if(number < 0 && errno != EINTR) {
            cout << "epoll failure" << endl;
            break;
        }
        for(int i = 0; i < number; ++i) {
            int sockFd = events[i].data.fd;
            // listenFd有事件发生
            if(sockFd == listenFd) {
                struct sockaddr_in clientAddr;
                socklen_t clientAddrLen = sizeof(clientAddr);
                int connFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientAddrLen);
                addFd(epollFd, connFd);
                users[connFd].address = clientAddr;
                users[connFd].sockFd = connFd;

                utilTimer* timer = new utilTimer;
                timer->userData = &users[connFd];
                timer->cb = callBackFunc;
                time_t cur = time(nullptr);
                timer->expire = cur + 3 * TIMESLOT;
                users[connFd].timer = timer;
                timerList.addTimer(timer);
            }
            // 统一事件源上有读事件发生
            else if(sockFd == pipeFd[0] && events[i].events & EPOLLIN) {
                int sig;
                char signals[1024];
                ret = recv(pipeFd[0], signals, sizeof(signals), 0);
                if(ret == -1) {
                    // handle the error
                    continue;
                }
                else if (ret == 0) {
                    continue;
                }
                else {
                    for(int i = 0; i < ret; ++i) {
                        switch(signals[i]){
                            case SIGALRM: {
                                timeout == true;
                                break;
                            }
                            case SIGTERM: {
                                stopServer == true;
                            }
                        }
                    }
                }
            }
            // 客户端Fd读事件发生
            else if (events[i].events & EPOLLIN) {
                memset(users[sockFd].buf, 0, BUFFER_SIZE);
                ret = recv(sockFd, users[sockFd].buf, BUFFER_SIZE - 1, 0); 
                cout << "get " << ret << " bytes of client data " << users[sockFd].buf 
                     << " from " << sockFd << endl;
                
                utilTimer* timer = users[sockFd].timer;
                if(ret < 0) {
                    if (errno != EAGAIN) {
                        callBackFunc(&users[sockFd]);
                        if(timer) {
                            timerList.delTimer(timer);
                        }
                    }
                }
                else if (ret == 0) {
                    callBackFunc(&users[sockFd]);
                    if(timer) {
                        timerList.delTimer(timer);
                    }
                }
                else {
                    if (timer) {
                        time_t cur = time(nullptr);
                        timer->expire = cur + 3 * TIMESLOT;
                        cout << "adjust timer once" << endl;
                        timerList.adjustTimer(timer);
                    }
                    else {
                        // 该sockFd对应的定时器任务不存在，说明出了问题，看是要新建任务添加进去还是干别的
                        /*
                        utilTimer* timer = new utilTimer;
                        timer->userData = &users[sockFd];
                        timer->cb = callBackFunc;
                        timer_t cur = time(nullptr);
                        timer->expire = cur + 3 * TIMESLOT;
                        users[sockFd].timer = timer;
                        timerList.addTimer(timer);
                        */
                    }
                } 
            }
            if (timeout) {
                timerHandler();
                timeout = false;
            }
        }
    }

    close(listenFd);
    close(pipeFd[1]);
    close(pipeFd[0]);
    delete[] users;
    return 0;
}